/*
 * 描述： <描述>
 * 修改人： rain
 * 修改时间： 2016年8月9日
 * 项目： rainhy-servicelogger
 */
package com.rainhy.servicelogger.builder.springdata;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Table;

import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.orm.jpa.JpaTransactionManager;

import com.rainhy.core.util.AssertUtils;
import com.rainhy.servicelogger.builder.BaseServiceLoggerBuilder;
import com.rainhy.servicelogger.context.LogLevel;
import com.rainhy.servicelogger.context.LogSource;
import com.rainhy.servicelogger.context.ServiceLoggerSession;
import com.rainhy.servicelogger.logger.ServiceLogDecorate;
import com.rainhy.servicelogger.logger.ServiceLogPersister;
import com.rainhy.servicelogger.logger.ServiceLogQuerier;
import com.rainhy.webcore.holder.WebContextHolder;
import com.rainhy.webcore.transaction.context.TransactionContext;
import com.rainhy.webcore.util.UUIDUtils;

/**
 * 业务日志建造者<br>
 * 生成数据库的业务日志<br>
 * 事务传播:REQUIRED(如果当前线程有绑定事务,则使用此事务;否则,新建一个事务,绑定到当前线程,再使用)
 * 
 * @author rain
 * @version [版本号, 2016年8月9日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class SpringDataServiceLoggerBuilder extends BaseServiceLoggerBuilder implements InitializingBean, ApplicationContextAware {
    
    /** Jpa 事务管理器 */
    private JpaTransactionManager transactionManager;
    
    // 注入的 EntityManager 事务和资源是交由 spring 容器来管理的,不用手动管理
    /** 实体管理器 */
    @PersistenceContext
    protected EntityManager entityManager;
    
    /** 业务日志包装器 */
    private ServiceLogDecorate decorate;
    
    /** 业务日志持久器 */
    private ServiceLogPersister persister;
    
    /** 业务日志查询器 */
    private ServiceLogQuerier querier;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        if (transactionManager == null) {
            transactionManager = applicationContext.getBean(JpaTransactionManager.class);
        }
        AssertUtils.notNull(transactionManager, "在使用 spring-data-jpa 时必须注入 JpaTransactionManager.");
        AssertUtils.notNull(entityManager, "请使用 spring-data-jpa.");
    }
    
    /** 业务日志包装器 */
    public void setDecorate(ServiceLogDecorate decorate) {
        this.decorate = decorate;
    }
    
    /** 业务日志持久器 */
    public void setPersister(ServiceLogPersister persister) {
        this.persister = persister;
    }
    
    /** 业务日志查询器 */
    public void setQuerier(ServiceLogQuerier querier) {
        this.querier = querier;
    }
    
    /** Jpa事务管理 */
    public void setTransactionManager(JpaTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }
    
    @Override
    protected ServiceLogDecorate buildServiceLoggerDecorate(final Class<?> loggerType) {
        if (this.decorate != null) {
            return this.decorate;
        }
        return new ServiceLogDecorate() {
            @Override
            public Object decorate(Object log) {
                SpringDataServiceLog sLog = (SpringDataServiceLog) log;
                if (StringUtils.isBlank(sLog.getId())) {    // id
                    sLog.setId(UUIDUtils.generateUUID());
                }
                if (sLog.getCreatedDate() == null) { // 创建时间
                    sLog.setCreatedDate(DateTime.now());
                }
                if (sLog.getLevel() == null) {   // 等级
                    sLog.setLevel(LogLevel.INFO);
                }
                if (sLog.getSource() == null) {   // 来源
                    sLog.setSource(LogSource.操作);
                }
                ServiceLoggerSession session = ServiceLoggerSession.getSession();
                if (session != null && session.isInit()) {
                    if (StringUtils.isBlank(sLog.getClientIpAddress())) {    // 客户端ip
                        sLog.setClientIpAddress(WebContextHolder.getRemoteHost());
                    }
                }
                return log;
            }
        };
    }
    
    @Override
    protected ServiceLogPersister buildServiceLoggerPersister(final Class<?> loggerType) {
        if (this.persister != null) {
            return this.persister;
        }
        return new ServiceLogPersister() {
            @Override
            public void persist(final Object log) {
                TransactionContext.execute(new Runnable() {
                    @Override
                    public void run() {
                        entityManager.persist(log);
                    }
                });
            }
        };
    }
    
    @Override
    protected ServiceLogQuerier buildServiceLoggerQuerier(final Class<?> loggerType) {
        if (this.querier != null) {
            return this.querier;
        }
        return new ServiceLogQuerier() {
            @Override
            public Object find(String key) {
                return entityManager.find(loggerType, key);
            }
            
            @Override
            public boolean isSupportQuery() {
                return true;
            }
        };
    }
    
    @Override
    protected boolean isSupport(Class<?> loggerType) {
        Table table = loggerType.getAnnotation(Table.class);
        Entity entity = loggerType.getAnnotation(Entity.class);
        
        if (table != null && entity != null && SpringDataServiceLog.class.isAssignableFrom(loggerType)) {
            return true;
        }
        return false;
    }
    
    /** spring容器 */
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    
}
